import random

from django.shortcuts import render, HttpResponse
from django.conf import settings
from django import forms
from web import models
from web.forms.bootstrap import BootStrapForm
from utils.tencent.sms import send_sms_single
from utils import encrypt
from django_redis import get_redis_connection
from django.conf import settings
from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError


class RegisterModelForm(BootStrapForm, forms.ModelForm):
    """ 用户注册 """
    # 给手机号添加正侧表达式
    mobile_phone = forms.CharField(label='手机号', validators=[RegexValidator(r'^(1[3|4|5|6|7|8|9])\d{9}$', '手机号格式错误'), ])
    password = forms.CharField(
        label='密码',
        min_length=6,
        max_length=64,
        error_messages={
            'min_length': "密码长度不能小于6个字符",
            'man_length': "密码长度不能大于64个字符",
        },
        widget=forms.PasswordInput)
    # 添加确认密码
    confirm_password = forms.CharField(
        label='确认密码',
        min_length=6,
        max_length=64,
        error_messages={
            'min_length': "重复密码长度不能小于6个字符",
            'man_length': "重复密码长度不能大于64个字符",
        },
        widget=forms.PasswordInput())
    # 添加验证码
    code = forms.CharField(
        label='验证码',
        widget=forms.TextInput())

    class Meta:
        model = models.UserInfo
        fields = "__all__"

    def clean_password(self):
        pwd = self.cleaned_data['password']
        # 密码加密并且返回
        return encrypt.md5(pwd)

    # 密码验证
    def clean_confirm_password(self):
        # pwd = self.cleaned_data['password']
        pwd = self.cleaned_data.get('password')
        confirm_pwd = encrypt.md5(self.cleaned_data['confirm_password'])
        if pwd != confirm_pwd:
            raise ValidationError("两次密码不一致！")
        return confirm_pwd

    # 手机号验证
    def clean_mobile_phone(self):
        mobile_phone = self.cleaned_data['mobile_phone']
        exists = models.UserInfo.objects.filter(mobile_phone=mobile_phone).exists()
        if exists:
            raise ValidationError("手机号已存在！")
        return mobile_phone

    # 验证码校验
    def clean_code(self):
        code = self.cleaned_data['code']  # 获取用户输入验证码
        # mobile_phone = self.cleaned_data['mobile_phone']  # 获取用户输入手机号
        mobile_phone = self.cleaned_data.get('mobile_phone')
        if not mobile_phone:
            return code
        conn = get_redis_connection()  # 与redis建立连接，获取发送的验证码
        redis_code = conn.get(mobile_phone)
        if not redis_code:
            raise ValidationError('验证码失效或没发送，请重试！')
        redis_str_code = redis_code.decode('utf-8')  # 将发送的验证码转换为字符串
        if code.strip() != redis_str_code:  # .strip()去掉左右两边的空格
            raise ValidationError("验证码错误，请重新输入！")
        return code


class SendSmsForm(forms.Form):
    mobile_phone = forms.CharField(label='手机号', validators=[RegexValidator(r'^(1[3|4|5|6|7|8|9])\d{9}$', '手机号格式错误'), ])

    # 重写__init__方法，因为在Form中无法使用request所以重写此方法并接受views.py传过来的request就可使用
    def __init__(self, request, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.request = request

    def clean_mobile_phone(self):
        # 手机号校验的钩子
        mobile_phone = self.cleaned_data['mobile_phone']

        # 判断短信模板是否有误
        tpl = self.request.GET.get('tpl')
        template_id = settings.TENCENT_SMS_TEMPLATE.get(tpl)
        if not template_id:
            raise ValidationError('短信模板有误！')

        # 校验数据库中是否已经有手机号
        exists = models.UserInfo.objects.filter(mobile_phone=mobile_phone).exists()
        if tpl == 'login':
            if not exists:
                raise ValidationError("手机号不存在哎！")
        else:
            if exists:
                raise ValidationError("手机号已经存哎！")

        code = random.randrange(1000, 9999)
        # 发送短信
        sms = send_sms_single(mobile_phone, template_id, [code, ])
        if sms['result'] != 0:
            raise ValidationError("短信发送失败,{}".format(sms['errmsg']))

        # 验证码写入redis中
        conn = get_redis_connection()
        conn.set(mobile_phone, code, ex=60)  # 将手机号和验证码存放到redis当中，时间为60s

        return mobile_phone


class LoginSmsForm(BootStrapForm, forms.Form):
    mobile_phone = forms.CharField(
        label='手机号',
        validators=[RegexValidator(r'^(1[3|4|5|6|7|8|9])\d{9}$', '手机号格式错误'), ])

    code = forms.CharField(
        label='验证码',
        widget=forms.TextInput())

    def clean_mobile_phone(self):
        mobile_phone = self.cleaned_data['mobile_phone']
        exists = models.UserInfo.objects.filter(mobile_phone=mobile_phone).exists()
        # user_object = models.UserInfo.objects.filter(mobile_phone=mobile_phone).first()
        if not exists:
            raise ValidationError("手机号不存在！")
        return mobile_phone

    def clean_code(self):
        code = self.cleaned_data['code']
        mobile_phone = self.cleaned_data.get('mobile_phone')
        # 手机号不存在，验证码就无需再校验
        if not mobile_phone:
            return code

        conn = get_redis_connection()  # 与redis建立连接，获取发送的验证码
        redis_code = conn.get(mobile_phone)
        if not redis_code:
            raise ValidationError('验证码失效或没发送，请重试！')
        redis_str_code = redis_code.decode('utf-8')  # 将发送的验证码转换为字符串
        if code.strip() != redis_str_code:  # .strip()去掉左右两边的空格
            raise ValidationError("验证码错误，请重新输入！")
        return code


class LoginForm(BootStrapForm, forms.Form):
    username = forms.CharField(label="手机或邮箱号")
    password = forms.CharField(label="密码", widget=forms.PasswordInput())
    code = forms.CharField(label="图片验证码")

    def __init__(self, request, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.request = request

    def clean_password(self):
        pwd = self.cleaned_data['password']
        # 密码加密并且返回
        return encrypt.md5(pwd)

    def clean_code(self):
        """钩子 图片验证码是否正确"""
        # 读取用户输入的验证码
        code = self.cleaned_data['code']

        # 去session中获取自己的验证码
        session_code = self.request.session.get('image_code')
        if not session_code:
            raise ValidationError("验证码已过期，请重新获取！")
        if code.upper() != session_code.upper():
            raise ValidationError("验证码输入错误！")
        return code
